package com.zjj.learn.algorithmPlus.segment1.linklist;

import java.util.Iterator;
import java.util.function.Consumer;

/**
 * 双向链表
 * 有哨兵节点，哨兵节点是 head 节点，head 节点后面的数据才是真正的数据
 *
 * @author zjj_admin
 */
public class DoublyLinkedListSentinel implements Iterable<Integer> {

    /**
     * 头哨兵
     */
    private Node head;
    /**
     * 尾哨兵
     */
    private Node tail;

    public DoublyLinkedListSentinel() {
        this.head = new Node(null, Integer.MIN_VALUE, null);
        this.tail = new Node(null, Integer.MAX_VALUE, null);
        // 让头哨兵和尾哨兵相连
        head.next = tail;
        tail.prev = head;
    }


    /**
     * 遍历链表，使用函数式接口 Consumer
     */
    public void loop(Consumer<Integer> consumer) {
        Node p = head.next;
        //当前节点不为 null 就一直循环
        while (p != tail) {
            // accept 里面传入相关的参数
            consumer.accept(p.value);
            p = p.next;
        }
    }


    /**
     * 在链表的头部插入一个数据
     *
     * @param value 待插入数据
     */
    public void addFirst(int value) {
        Node next = head.next;
        Node inserted = new Node(head, value, next);
        //让上一个节点的 next 指针指向带插入节点
        head.next = inserted;
        //让下一个极点的 prev 指针指向待插入节点
        next.prev = inserted;
    }


    /**
     * 添加数据到尾结点
     *
     * @param value
     */
    public void addLast(int value) {
        Node last = findLast();
        if (last == null) {
            //没有数据
            addFirst(value);
            return;
        }
        //最后节点的下一个节点，一般是 tail 节点
        Node next = last.next;
        Node inserted = new Node(last, value, next);
        last.next = inserted;
        //让下一个极点的 prev 指针指向待插入节点
        next.prev = inserted;
    }

    /**
     * 找到当前链表的最后一个有效节点
     *
     * @return
     */
    private Node findLast() {
        if (head.next == tail) {
            return null;
        }
        Node p = head.next;
        while (p != null) {
            if (p.next == tail) {
                return p;
            }
            p = p.next;
        }
        return null;
    }

    /**
     * 获取链表的有效长度
     *
     * @return
     */
    public int size() {
        Node p = head.next;
        int size = 0;
        while (p != tail) {
            size++;
            p = p.next;
        }
        return size;
    }

    /**
     * 获取链表中的所有数据
     *
     * @return 数据数组
     */
    public int[] values() {
        int size = size();
        int[] res = new int[size];
        Node p = head.next;
        int i = 0;
        while (p != tail) {
            res[i] = p.value;
            p = p.next;
            i++;
        }
        return res;
    }


    /**
     * 获取索引为 index 对应节点的数据
     *
     * @param index
     * @return
     */
    public int get(int index) {
        Node node = getNode(index);
        if (node == head || node == tail || node == null) {
            throw new IndexOutOfBoundsException("index：" + index + " 越界了");
        }
        return node.value;
    }


    /**
     * 获取索引为 index 对应节点
     * 头哨兵的索引为 -1
     *
     * @param index
     * @return
     */
    private Node getNode(int index) {
        int i = -1;
        Node p = head;
        while (p != null) {
            if (i == index) {
                return p;
            }
            i++;
            p = p.next;
        }
        return null;
    }


    /**
     * 在指定的索引位置添加数据
     *
     * @param index
     * @param value
     */
    public void add(int index, int value) {
        //获取上一个节点
        Node prev = getNode(index - 1);
        if (prev == null) {
            throw new IndexOutOfBoundsException("index：" + index + " 越界了");
        }
        Node next = prev.next;
        //需要插入的节点
        Node inserted = new Node(prev, value, next);
        //让上一个节点的 next 指针指向带插入节点
        prev.next = inserted;
        //让下一个极点的 prev 指针指向待插入节点
        next.prev = inserted;
    }

    /**
     * 移除链表中的第一个元素
     *
     * @return
     */
    public int removeFirst() {
        Node p = head.next;
        if (p == tail) {
            throw new NullPointerException("链表没有数据了");
        }
        Node next = p.next;
        head.next = next;
        next.prev = head;
        return p.value;
    }

    /**
     * 根据索引数据
     *
     * @param index 删除索引
     * @return 被删除的索引节点数据
     */
    public int remove(int index) {
        //获取待删除的节点
        Node removed = getNode(index);
        if (removed == null || removed == head || removed == tail) {
            throw new IndexOutOfBoundsException("index：" + index + " 越界了");
        }
        Node prev = removed.prev;
        Node next = removed.next;
        prev.next = next;
        next.prev = prev;
        return removed.value;
    }

    /**
     * 基于迭代器进行循环
     *
     * @return
     */
    @Override
    public Iterator<Integer> iterator() {
        return new Iterator<Integer>() {
            Node p = head.next;

            @Override
            public boolean hasNext() {
                return p != tail;
            }

            @Override
            public Integer next() {
                int v = p.value;
                p = p.next;
                return v;
            }
        };
    }

    /**
     * 链表中的节点，链表和节点是组合的关系，所以做成内部内比较合适。
     */
    private static class Node {

        /**
         * 上一个节点
         */
        Node prev;
        /**
         * 节点数据
         */
        int value;


        /**
         * 当前节点的下一个指针
         */
        Node next;

        public Node(Node prev, int value, Node next) {
            this.prev = prev;
            this.value = value;
            this.next = next;
        }
    }

}
